﻿#include "remoteobject.h"
#include "remoteobject_p.h"
#include "remoteobjecthost.h"
#include "metaproperty.h"
#include "metafunction.h"
#include "metasignal.h"

RemoteObjectHost::RemoteObjectHost(RemoteObjectHostPrivate *P):RemoteObjectNodeBase(P)
{

}

RemoteObjectHost::RemoteObjectHost():RemoteObjectHost(new RemoteObjectHostPrivate())
{

}

RemoteObjectHost::~RemoteObjectHost()
{

}

void RemoteObjectHost::Listen(std::string hostName)
{
    if(_d()->isActive){
        return;
    }

    int pos = hostName.find(":");

    std::string ip(hostName.begin(),hostName.begin() + pos);
    int port = std::atoi(std::string(hostName.begin()+pos + 1,hostName.end()).c_str());

    _P->channel.Bind(port);
}

void RemoteObjectHost::close()
{

}

void RemoteObjectHost::enableRemoting(RemoteObject *obj)
{
    if(obj){
        enableRemoting(obj,obj->GetMetaInfo().ClassName());
    }
}

void RemoteObjectHost::enableRemoting(RemoteObject *obj, std::string shareName)
{
    if(!obj){
        return;
    }

    if(obj->GetStatus() != RemoteObject::Status::S_None || obj->GetRole() != RemoteObject::Role::R_Independent)
    {
        return;
    }

    if(_d()->pool.count(shareName)){
        return;
    }

    MetaInfo meta = obj->GetMetaInfo();
    DynamicMetaInfoDataPtr meta_data;
    std::string className = meta.ClassName();
    if(!_d()->metadata.count(className))
    {
        meta_data = std::make_shared<DynamicMetaInfoData>(ExtractMetaInfo(meta));
       _d()->metadata[className] = meta_data;
    }
    else{
        meta_data = _d()->metadata[className];
    }

    RemoteObjectHostData data;
    data.obj = obj;
    data.shareName = shareName;
    data.meta = meta_data;
    data.registry = std::make_shared<ConnectionRegistry>();

    for(auto i:meta_data->SignalDatas)
    {
        int signal_index = i.first;
        auto metasignal = meta.GetSignal(signal_index);
        auto types = std::shared_ptr<std::vector<MetaTypeId>>(new std::vector<MetaTypeId>(metasignal.GetParamTypes()));

		*data.registry += metasignal.GetSignal(dynamic_cast<void*>(obj))->UnSafeBind([=](const void** param) {
			slotOnSignalInvoked(obj, signal_index, param, *types);
			}, this);
    }

    for(auto i:meta_data->Properties){
        int property_index = i.first;
        auto metaproperty = meta.GetProperty(property_index);
        auto metasignal = metaproperty.GetNotifier();
        auto type = metasignal.GetParamTypes()[0];
        *data.registry += metasignal.GetSignal(dynamic_cast<void*>(obj))->UnSafeBind([=](const void** param){
            slotOnPropertyChanged(obj,property_index,param[0],type);
        },this);

    }

    _d()->pool[shareName] = data;

    obj->SetRole(RemoteObject::R_Source);
    obj->SetStatus(RemoteObject::S_Normal);
    StartRemotingNotify(shareName);
}

void RemoteObjectHost::stopRemoting(RemoteObject * obj)
{
}

RemoteObject * RemoteObjectHost::acquire(std::string sharedName)
{
	if (_d()->pool.count(sharedName)) {
		return _d()->pool[sharedName].obj;
	}
	return nullptr;
}

void RemoteObjectHost::StartRemotingNotify(const std::string &shareName)
{
    auto &data =  _d()->pool[shareName];

    for(auto &i:_d()->clients){
        ClientId id = i.first;

		SendNewObject(id, &data);

        if(i.second.Registered.count(shareName))
        {
            SendMetaReply(id,&data);
            SendObjectReply(id,&data);
        }
    }

}

void RemoteObjectHost::SendObjectList(ClientId id)
{
	ReplyObjectList m;

	for (auto i : _d()->pool) {
		m.objs[i.second.shareName] = i.second.obj->GetMetaInfo().ClassName();
	}

	SendMessage(&m, id);
}

void RemoteObjectHost::SendObjectReply(ClientId id, RemoteObjectHostData *data)
{
    ReplyObject m;

    RemoteObject* obj = data->obj;

    MetaInfo meta = obj->GetMetaInfo();

    DynamicMetaInfoDataPtr meta_data = data->meta;

    for(auto &i : meta_data->Properties)
    {
        int index = i.first;
        Variant value = meta.GetProperty(index).GetValue(dynamic_cast<void*>(obj));
		OutputValueFix(value);
        m.propertys[index] = value;
    }

    m.shareName = data->shareName;

    SendMessage(&m,id);
}

void RemoteObjectHost::SendMetaReply(ClientId id, RemoteObjectHostData *data)
{
    ReplyObjectMeta m;

    m.meta = *data->meta;
    m.shareName = data->shareName;

    SendMessage(&m,id);
}

void RemoteObjectHost::SendNewObject(ClientId id, RemoteObjectHostData * data)
{
	NewObject m;

	m.className = data->meta->className;
	m.shareName = data->shareName;

	SendMessage(&m, id);
}

RemoteObjectHostData *RemoteObjectHost::FindObjectData(RemoteObject *obj)
{
    for(auto &i:_d()->pool){
        if(i.second.obj == obj)
        {
            return &i.second;
        }
    }

    return NULL;
}

void RemoteObjectHost::slotOnSignalInvoked(RemoteObject* obj,int index,const void **param,const std::vector<MetaTypeId> types)
{
    RemoteObjectHostData* data = FindObjectData(obj);

    if(!data) return;

    MetaInfo meta = obj->GetMetaInfo();


    SignalInvoked m;


    m.shareName = data->shareName;
    m.index = index;
    for(int i = 0;i<types.size();i++){
        m.args.push_back(Variant(param[i],types[i]));
    }

    for(auto i:_d()->clients){
        ClientId id = i.first;
        if(i.second.Registered.count(data->shareName)){
            SendMessage(&m,id);
        }
    }
}

RemoteObjectClientData *RemoteObjectHost::GetOrAcquireClientData(ClientId id)
{
    if(!_d()->clients.count(id)){
        RemoteObjectClientData data;

        data.id = id;

        _d()->clients[id] = data;
    }

    return &_d()->clients[id];
}

void RemoteObjectHost::slotOnPropertyChanged(RemoteObject *source, int index,const void* value,MetaTypeId type)
{
    Variant v(value,type);

    auto objdata = FindObjectData(source);

    PropertyUpdate update;

    update.shareName = objdata->shareName;
    update.propertyId = index;
	OutputValueFix(v);
    update.value = v;

    for(auto &i:_d()->clients){
        ClientId id = i.first;
        auto &clientdata = i.second;
        if(clientdata.Registered.count(objdata->shareName))
        {
            SendMessage(&update,id);
        }
    }
}

void RemoteObjectHost::OutputValueFix(Variant & value)
{
	MetaType m = value.TypeId();

	//指针不能直接在网络中使用，需要修正一下
	if (m.isPointerToReflectable()) {
		RemoteObject* obj = dynamic_cast<RemoteObject*>(value.Value<Reflectable*>());
		std::string shareName;
		if (obj) {
			auto data = FindObjectData(obj);
			shareName = data->shareName;
		}

		value = Variant::FromValue(shareName);
	}
}

void RemoteObjectHost::InputValueFix(Variant & value, MetaTypeId targetType)
{
	MetaType m = targetType;

	if (m.isPointerToReflectable()) {
		std::string shareName = value.Value<std::string>();
		RemoteObject* obj = NULL;
		if (_d()->pool.count(shareName)) {
			obj = _d()->pool[shareName].obj;
		}

		value = Variant::FromValue(obj);
		value.Convert(targetType);
	}
}

void RemoteObjectHost::OnMessage(Message *message)
{
    ClientId id = message->source;
    RemoteObjectClientData* data = GetOrAcquireClientData(id);
	if (message->type == RO_RequestObjectList) {

	}
    else if(message->type == RO_RequestObject){
        auto req = dynamic_cast<RequestObject*>(message);

        data->Registered.insert(req->shareName);

        if(_d()->pool.count(req->shareName)){
            auto &objData = _d()->pool[req->shareName];
            SendObjectReply(id,&objData);
        }
    }
    else if(message->type == RO_RequestObjectMeta){
        auto req = dynamic_cast<RequestObjectMeta*>(message);

        data->Registered.insert(req->shareName);
        if(_d()->pool.count(req->shareName)){
            auto &objData = _d()->pool[req->shareName];
            SendMetaReply(id,&objData);
        }
    }
    else if(message->type == RO_UnRegisterObject){
        auto req = dynamic_cast<UnRegisterObject*>(message);
        data->Registered.erase(req->shareName);
    }
    else if(message->type == RO_PropertyPush){
        auto req = dynamic_cast<PropertyPush*>(message);

        if(_d()->pool.count(req->shareName)){
            auto &objData = _d()->pool[req->shareName];
            if(objData.meta->Properties.count(req->idx))
            {
                MetaInfo meta = objData.obj->GetMetaInfo();
				auto prop = meta.GetProperty(req->idx);
				Variant value = req->value;
				InputValueFix(value, prop.Type());
                prop.SetValue(dynamic_cast<void*>(objData.obj),value);
            }
        }
    }
    else if(message->type == RO_SlotRequest)
    {
        auto req = dynamic_cast<SlotRequest*>(message);

        if(_d()->pool.count(req->shareName)){
            auto &objData = _d()->pool[req->shareName];
            if(objData.meta->Functions.count(req->funIdx))
            {
                auto meta = objData.obj->GetMetaInfo();
                auto function = meta.GetFunction(req->funIdx);

				Variant ret;

				function.DynamicInvoke(dynamic_cast<void*>(objData.obj), ret, req->arguments);

                if(req->response){
                    SlotFinished m;

                    m.ReplyId = req->RequestId;
                    m.ret = ret;

                    SendMessage(&m,id);
                }
            }
        }
    }
}

void RemoteObjectHost::UnRegister(RemoteObject *obj)
{

}

RemoteObjectHostPrivate *RemoteObjectHost::_d()
{
    return static_cast<RemoteObjectHostPrivate*>(_P.get());
}
